﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TelnetRouterApplication.Model;
using LukeSkywalker.IPNetwork;

namespace TelnetRouterApplication.Control
{
    public class CommandHandler
    {
        private static bool _DEBUG = false;
        public static bool DEBUG
        {
            get { return CommandHandler._DEBUG; }
            set { CommandHandler._DEBUG = value; }
        }

        private static CommandHandler instance;
        private CommandHandler() { }

        public static CommandHandler GetInstance()
        {
            if (instance == null)
            {
                instance = new CommandHandler();
            }
            return instance;
        }

        public SettingsModel GetSettings()
        {
            List<string> settingsList = new List<string>();
            string messageTotal = "";
            SettingsModel settingsModel = null;

            //Primeiro devemos estar no modo enabled
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            //Depois devemos mandar uma mensagem de show run
            ConnectionHandler.GetInstance().SendMessage("show run");
            Thread.Sleep(300);

            bool endReached = false;
            int loopEnder = 0;
            while (!endReached)
            {
                ConnectionHandler.GetInstance().Delay(200);

                //Recebe as mensagens sem dar o split
                List<string> messages = ConnectionHandler.GetInstance().ReceiveMessage(false);

                if (messages.Count > 0)
                {
                    //Procura por um # nas mensagens recebidas para saber se terminaram as mensagens do show run
                    if (Contains(messages, "#") || loopEnder == 10)
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("'#' encontrado!");
                        }
                        endReached = true;
                    }
                    else
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("Não foi achado '#' nas mensagens.");
                        }
                    }
                    messageTotal += messages[0];
                }
                ConnectionHandler.GetInstance().SendMessageNoEnter(" ");
                loopEnder++;
            }

            messageTotal = messageTotal.Replace("\n", "");
            settingsList = new List<string>(messageTotal.Split('\r'));

            if(DEBUG)
            {
                System.Diagnostics.Debug.WriteLine("======================Settings======================");
                int counter = 1;
                foreach(string setting in settingsList)
                {
                    System.Diagnostics.Debug.WriteLine("Settings(" + counter + "): " + setting);
                    counter++;
                }
            }

            ConnectionHandler.GetInstance().SendMessage("\r\n");

            //Transformando a lista de mensagens recebidas em um modelo de Settings
            settingsModel = new SettingsModel(settingsList);

            //Chamar o método para pegar o clock do roteador
            string clock = GetRouterClockTime();
            settingsModel.Clock = clock;

            //Transformar o tempo vindo do roteador no formato correto para ser exibido
            string maskedClock = SettingsModel.ClockToMasked(clock);
            settingsModel.MaskedClock = maskedClock;

            return settingsModel;
        }

        public bool PingInterface(Interface inter)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            string pingIp = inter.Ip;

            if (pingIp.Equals("unassigned") || pingIp.Equals(""))
            {
                return false;
            }

            ConnectionHandler.GetInstance().SendMessage("ping " + pingIp);

            //Precisa esperar 2 segundos, porque é o máximo de tempo que vai demorar para dar o ping
            ConnectionHandler.GetInstance().Delay(2000);

            List<string> messages = ConnectionHandler.GetInstance().ReceiveMessage();

            string result = "";
            foreach (string message in messages)
            {
                //A frase é "Sucess rate is X percent (0/5)"
                if (message.Contains("Success"))
                {
                    result = message;
                }
            }

            if (!result.Equals(""))
            {
                string[] parameters = result.Split(' ');

                //É o terceiro parâmetro que contém a porcentagem

                float percent = float.Parse(parameters[3]);

                if (percent > 0)
                {
                    return true;
                }
            }

            return false;
        }

        public bool AddRoute(Route route)
        {
            return AddRoute(route.Network, route.Mask, route.WhereToGo);
        }

        public void AddVLAN(VLAN vlan)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            //Entrar no VLAN
            ConnectionHandler.GetInstance().SendMessage("vlan database");
            ConnectionHandler.GetInstance().ReceiveMessage();

            ConnectionHandler.GetInstance().SendMessage("vlan " + vlan.Number + " name " + vlan.Name);
            ConnectionHandler.GetInstance().ReceiveMessage();

            //Saindo do VLAN
            ConnectionHandler.GetInstance().SendMessage("exit");
            ConnectionHandler.GetInstance().ReceiveMessage();
        }

        public void DeleteVLAN(VLAN vlan)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            //Entrar no VLAN
            ConnectionHandler.GetInstance().SendMessage("vlan database");
            ConnectionHandler.GetInstance().ReceiveMessage();

            ConnectionHandler.GetInstance().SendMessage("no vlan " + vlan.Number);
            ConnectionHandler.GetInstance().ReceiveMessage();

            //Saindo do VLAN
            ConnectionHandler.GetInstance().SendMessage("exit");
            ConnectionHandler.GetInstance().ReceiveMessage();
        }

        public bool DeleteRoute(Route route)
        {
            return DeleteRoute(route.Network, route.Mask, route.WhereToGo);
        }

        public bool AddRoute(IpModel destination, IpModel mask, IpModel whereToGo)
        {
            return RouteCommand(false, destination, mask, whereToGo);
        }

        public bool DeleteRoute(IpModel destination, IpModel mask, IpModel whereToGo)
        {
            return RouteCommand(true, destination, mask, whereToGo);
        }

        //Era bom mudar o nome desse método para algo mais claro, como "DeleteRouteCommand"
        public bool RouteCommand(bool delete, IpModel destination, IpModel mask, IpModel whereToGo)
        {
            //Primeiro devemos estar no modo config
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.config);

            string commandRoute = delete ? "no " : "";
            commandRoute += "ip route " + destination.ip + " " + mask.ip + " " + whereToGo.ip;

            ConnectionHandler.GetInstance().SendMessage(commandRoute);

            List<string> returnedMessage = ConnectionHandler.GetInstance().ReceiveMessage();

            foreach (string message in returnedMessage)
            {
                if (message.ToLower().Contains("incomplete") || message.ToLower().Contains("invalid") || message.ToLower().Contains("inconsistent"))
                {
                    return false;
                }
            }
            return true;
        }

        public bool RouteRipAddNetworkCommand(IpModel network)
        {
            return RouteRipNetworkCommand(false, network);
        }

        public bool RouteRipDeleteNetworkCommand(IpModel network)
        {
            return RouteRipNetworkCommand(true, network);
        }

        public bool RouteRipNetworkCommand(bool delete, IpModel network)
        {
            RouteRipCommand();

            string commandRoute = delete ? "no " : "";
            commandRoute += "network " + network.ip;

            ConnectionHandler.GetInstance().SendMessage(commandRoute);

            List<string> returnedMessage = ConnectionHandler.GetInstance().ReceiveMessage();

            foreach (string message in returnedMessage)
            {
                if (message.ToLower().Contains("incomplete") || message.ToLower().Contains("invalid") || message.ToLower().Contains("inconsistent"))
                {
                    return false;
                }
            }
            return true;
        }

        public void RouteRipCommand()
        {
            //Primeiro devemos estar no modo config
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.config);

            string commandRoute = "router rip";
            ConnectionHandler.GetInstance().SendMessage(commandRoute);
        }

        public void ChangeRouterName(string newName)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.config);

            //Depois devemos mandar uma mensagem para mudar o hostname
            ConnectionHandler.GetInstance().SendMessage("hostname "+newName);
            ConfigutarionHandler.GetInstance().RouterSettings.Hostname = newName;

            ConnectionHandler.GetInstance().ReceiveMessage();
        }

        public void ChangeRouterEnablePassword(string newPassword)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.config);

            //Depois devemos mandar uma mensagem para mudar o password do enable
            if(newPassword.Equals(""))
            {
                ConnectionHandler.GetInstance().SendMessage("no enable password");
            }
            else
            {
                ConnectionHandler.GetInstance().SendMessage("enable password " + newPassword);
            }

            
            ConfigutarionHandler.GetInstance().RouterSettings.EnablePassword = newPassword;

            ConnectionHandler.GetInstance().ReceiveMessage();
        }

        public void ChangeVTYPassword(string newPassword)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.configline, "line vty 0 4");

            if (newPassword.Equals(""))
            {
                ConnectionHandler.GetInstance().SendMessage("no password");
                ConnectionHandler.GetInstance().ReceiveMessage();
                ConnectionHandler.GetInstance().SendMessage("no login");
                ConnectionHandler.GetInstance().ReceiveMessage();
            }
            else
            {
                ConnectionHandler.GetInstance().SendMessage("password " + newPassword);
                ConnectionHandler.GetInstance().ReceiveMessage();
                ConnectionHandler.GetInstance().SendMessage("login");
                ConnectionHandler.GetInstance().ReceiveMessage();
            }
        }

        public string GetRouterClockTime()
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            ConnectionHandler.GetInstance().SendMessage("show clock");

            List<string> receivedMessages = ConnectionHandler.GetInstance().ReceiveMessage();

            string clock = "";
            if (receivedMessages.Count > 0)
            {
                clock = receivedMessages[1].Substring(1);
            }

            return clock;
        }

        public void ChangeRouterClockTime(string maskedClock)
        {
            //clock set 10:50:00 Oct 26 2006  (dentro do enable, clock set "horário no padrão do exemplo ao lado")

            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            string clock = SettingsModel.MaskedToClock(maskedClock);

            ConnectionHandler.GetInstance().SendMessage("clock set " + clock);

            ConnectionHandler.GetInstance().ReceiveMessage();
        }

        public List<Route> GetStaticRouteList()
        {
            SettingsModel settings = CommandHandler.GetInstance().GetSettings();
            return settings.Routes;
        }

        public List<Interface> GetInterfaces()
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            ConnectionHandler.GetInstance().SendMessage("show ip interface brief");
            ConnectionHandler.GetInstance().Delay(1000);

            //TODO mudar

            //List<string> messages = ConnectionHandler.GetInstance().ReceiveMessage();

            string messageTotal = "";
            List<string> messages = new List<string>();

            bool endReached = false;
            int loopEnder = 0;
            while (!endReached)
            {
                ConnectionHandler.GetInstance().Delay(200);

                //Recebe as mensagens sem dar o split
                messages = ConnectionHandler.GetInstance().ReceiveMessage(false);

                if (messages.Count > 0)
                {
                    //Procura por um # nas mensagens recebidas para saber se terminaram as mensagens do show run
                    if (Contains(messages, "#") || loopEnder == 10)
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("'#' encontrado!");
                        }
                        endReached = true;
                    }
                    else
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("Não foi achado '#' nas mensagens.");
                        }
                    }
                    messageTotal += messages[0];
                }
                ConnectionHandler.GetInstance().SendMessageNoEnter(" ");
                loopEnder++;
            }

            messageTotal = messageTotal.Replace("\n", "");
            messages = new List<string>(messageTotal.Split('\r'));

            return Interface.GetListInterface(messages);
        }

        public Interface GetInterface(Interface inter)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            ConnectionHandler.GetInstance().SendMessage("show controllers "+inter.Name);
            ConnectionHandler.GetInstance().Delay(300);

            List<string> messages = null;

            string messageTotal = "";
            bool endReached = false;
            int loopEnder = 0;
            while (!endReached)
            {
                ConnectionHandler.GetInstance().Delay(200);

                //Recebe as mensagens sem dar o split
                messages = ConnectionHandler.GetInstance().ReceiveMessage(false);

                if (messages.Count > 0)
                {
                    //Procura por um # nas mensagens recebidas para saber se terminaram as mensagens do show run
                    if (Contains(messages, "#") || loopEnder == 20)
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("'#' encontrado!");
                        }
                        endReached = true;
                    }
                    else
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("Não foi achado '#' nas mensagens.");
                        }
                    }
                    messageTotal += messages[0];
                }
                ConnectionHandler.GetInstance().SendMessageNoEnter(" ");
                loopEnder++;
            }

            ConnectionHandler.GetInstance().SendMessage("");
            string[] parameters = messageTotal.Split('\r');

            bool lineFound = false;
            int clockrate = 0;

            //Verificar se é Master ou Slave
            foreach (string parameter in parameters)
            {
                if (parameter.Contains("DTE"))
                {
                    inter.Master = true;
                    lineFound = true;
                }
                else if (parameter.Contains("DCE"))
                {
                    lineFound = true;
                }

                if (lineFound)
                {
                    string[] parameters2 = parameter.Split(' ');

                    //Se não tiver clock setado, vai ser no exemplo:
                    //-DCE 530, no clock
                    if (parameters2[parameters2.Length - 1].Equals("clock"))
                    {
                        clockrate = 0;
                    }
                    //Se tiver clock setado, vai ser no exemplo:
                    //-DCE 530, clock 2400
                    else
                    {
                        if(!parameters2[parameters2.Length - 1].Contains("et")){
                            clockrate = int.Parse(parameters2[parameters2.Length - 1]);
                        }
                        else
                        {
                            clockrate = 0;
                        }
                    }
                    lineFound = false;
                }
            }

            inter.Clockrate = clockrate;

            //Agora é para pegar o IP e máscara

            ConnectionHandler.GetInstance().SendMessage("show ip interface " + inter.Name);
            ConnectionHandler.GetInstance().Delay(300);

            messageTotal = "";
            endReached = false;
            loopEnder = 0;
            while (!endReached)
            {
                ConnectionHandler.GetInstance().Delay(200);

                //Recebe as mensagens sem dar o split
                messages = ConnectionHandler.GetInstance().ReceiveMessage(false);

                if (messages.Count > 0)
                {
                    //Procura por um # nas mensagens recebidas para saber se terminaram as mensagens do show run
                    if (Contains(messages, "#") || loopEnder == 20)
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("'#' encontrado!");
                        }
                        endReached = true;
                    }
                    else
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("Não foi achado '#' nas mensagens.");
                        }
                    }
                    messageTotal += messages[0];
                }
                ConnectionHandler.GetInstance().SendMessageNoEnter(" ");
                loopEnder++;
            }

            ConnectionHandler.GetInstance().ReceiveMessage();

            parameters = messageTotal.Split('\r');
            bool ipFound = false;
            foreach (string parameter in parameters)
            {
                if (parameter.Contains("Internet address"))
                {
                    string[] parameters2 = parameter.Split(' ');

                    string ip = parameters2[parameters2.Length - 1];

                    IPNetwork ipNetwork = IPNetwork.Parse(ip);

                    inter.Ip = ip.Split('/')[0];
                    inter.Mask = ipNetwork.Netmask.ToString();

                    ipFound = true;
                }
            }

            if (!ipFound)
            {
                inter.Ip = "unassigned";
                inter.Mask = "unassigned";
            }

            ConnectionHandler.GetInstance().SendMessage("\r\n\r\n");
            while(!ConnectionHandler.GetInstance().ReceiveMessage(false)[0].Contains('#'));

            ConnectionHandler.GetInstance().SendMessage("show ip interface brief " + inter.Name);
            ConnectionHandler.GetInstance().Delay(500);

            messages.Clear();
            messages = ConnectionHandler.GetInstance().ReceiveMessage(false);
            if (messages[0].Contains("up"))
            {
                inter.Shutdown = true;
                inter.Protocol = true;
            }
            else
            {
                inter.Shutdown = false;
                inter.Protocol = false;
            }

            ConnectionHandler.GetInstance().SendMessage("\r\n");
            ConnectionHandler.GetInstance().ReceiveMessage();

            return inter;
        }

        public List<VLAN> getListVLAN()
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);

            //Entrar no VLAN
            ConnectionHandler.GetInstance().SendMessage("vlan database");
            ConnectionHandler.GetInstance().ReceiveMessage();

            ConnectionHandler.GetInstance().SendMessage("show");

            List<string> messages = new List<string>();

            string messageTotal = "";
            bool endReached = false;
            int loopEnder = 0;
            while (!endReached)
            {
                ConnectionHandler.GetInstance().Delay(200);

                //Recebe as mensagens sem dar o split
                messages = ConnectionHandler.GetInstance().ReceiveMessage(false);

                if (messages.Count > 0)
                {
                    //Procura por um # nas mensagens recebidas para saber se terminaram as mensagens do show run
                    if (Contains(messages, "#") || loopEnder == 20)
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("'#' encontrado!");
                        }
                        endReached = true;
                    }
                    else
                    {
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("Não foi achado '#' nas mensagens.");
                        }
                    }
                    messageTotal += messages[0];
                }
                ConnectionHandler.GetInstance().SendMessageNoEnter(" ");
                loopEnder++;
            }
            string[] parameters = messageTotal.Split('\r');

            messages = new List<string>(parameters);

            //Saíndo da VLAN
            ConnectionHandler.GetInstance().SendMessage("exit");
            ConnectionHandler.GetInstance().ReceiveMessage();

            return VLAN.GetListVLAN(messages);
        }

        public void TurnInterface(bool turn, Interface inter)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.configif, "interface " + inter.Name);
            string turnOn = "";
            //Se turn for true, então é "no shutdown", se for false, é "shutdown", somente
            if (turn)
            {
                turnOn += "no ";
            }
            ConnectionHandler.GetInstance().SendMessage(turnOn + "shutdown");

            ConnectionHandler.GetInstance().ReceiveMessage();

            //Só por segurança
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);
        }

        public void ChangeInterfaceClockRate(int clockrate, Interface inter)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.configif, "interface " + inter.Name);

            if (clockrate == 0)
            {
                ConnectionHandler.GetInstance().SendMessage("no clock rate");
            }
            else
            {
                ConnectionHandler.GetInstance().SendMessage("clock rate " + clockrate);
            }

            ConnectionHandler.GetInstance().ReceiveMessage();

            //Só por segurança
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);
        }

        public void ChangeInterfaceIP(Interface inter)
        {
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.configif, "interface " + inter.Name);

            if (inter.Ip.Equals("") || inter.Master.Equals(""))
            {
                ConnectionHandler.GetInstance().SendMessage("no ip address");
            }
            else
            {
                ConnectionHandler.GetInstance().SendMessage("ip address " + inter.Ip + " " + inter.Mask);
            }

            ConnectionHandler.GetInstance().ReceiveMessage();

            //Só por segurança
            RouterLevelHandler.GetInstance().GoTo(RouterLevelHandler.Location.enabled);
        }

        private bool Contains(List<string> list, string value)
        {
            //Método para verificar se existe o valor passado na lista de strings
            foreach (string message in list)
            {
                /*
                if(DEBUG)
                {
                    System.Diagnostics.Debug.WriteLine("=>"+message);
                }
                */
                if(message.Equals(value) || message.Contains(value))
                {
                    return true;
                }
            }
            return false;
        }
    }
}
